package com.example.controller;

import com.example.common.Result;
import com.example.common.ResultUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.ui.common.security.SecurityUtils;
import org.flowable.ui.common.service.exception.BadRequestException;
import org.flowable.ui.common.service.exception.BaseModelerRestException;
import org.flowable.ui.common.service.exception.ConflictingRequestException;
import org.flowable.ui.common.service.exception.InternalServerErrorException;
import org.flowable.ui.modeler.domain.Model;
import org.flowable.ui.modeler.model.ModelKeyRepresentation;
import org.flowable.ui.modeler.model.ModelRepresentation;
import org.flowable.ui.modeler.repository.ModelRepository;
import org.flowable.ui.modeler.serviceapi.ModelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.List;

/**
 * 模型管理 创建/删除/发布
 */
@Slf4j
@RestController
@RequestMapping("models")
public class ModelerController{

    @Autowired
    RepositoryService repositoryService;

    @Autowired
    private ModelService modelService;

    @Autowired
    private ModelRepository modelRepository;

    /**
    * 功能描述: 新建一个流程模型
    * @author: 庞留杰
    * @param key:模型Key
    * @param name:模型名称
    * @param description:模型描述信息
    * @param modelType:0
    * @exception
    * @date: 2020/9/10 10:25
    */
    @PostMapping(value = "/rest/models", produces = "application/json")
    public Result createModel(@RequestBody ModelRepresentation modelRepresentation) {
        modelRepresentation.setKey(modelRepresentation.getKey().replaceAll(" ", ""));
        ModelKeyRepresentation modelKeyInfo = modelService.validateModelKey(null, modelRepresentation.getModelType(), modelRepresentation.getKey());
        if (modelKeyInfo.isKeyAlreadyExists()) {
            throw new ConflictingRequestException("模型的KEY已经存在: " + modelRepresentation.getKey());
        }
        String json = modelService.createModelJson(modelRepresentation);
        org.flowable.ui.modeler.domain.Model newModel = modelService.createModel(modelRepresentation, json, SecurityUtils.getCurrentUserObject());
        ModelRepresentation model = new ModelRepresentation(newModel);
        return new ResultUtil<>().setData(model);
    }


    /**
     * 功能描述: 查询所有流程模型
     * @author: 庞留杰
     * @param modelType:0
     * @param sort:modifiedDesc
     * @exception
     * @date: 2020/9/10 10:25
     */
    @GetMapping(value = "/rest/models", produces = "application/json")
    public Result getModels(@RequestParam(required = false) String sort, @RequestParam(required = false) Integer modelType) {
        List<Model> models = modelRepository.findByModelType(modelType, sort);
        return new ResultUtil<>().setData(models);
    }


//
//    @Override
//    public Object getOne(@PathVariable("id") String id) {
//        Model model = repositoryService.createModelQuery().modelId(id).singleResult();
//        return ToWeb.buildResult().setObjData(model);
//    }
//
//    @Override
//    public Object getList(@RequestParam(value = "rowSize", defaultValue = "1000", required = false) Integer rowSize, @RequestParam(value = "page", defaultValue = "1", required = false) Integer page) {
//        List<Model> list = repositoryService.createModelQuery().listPage(rowSize * (page - 1)
//                , rowSize);
//        long count = repositoryService.createModelQuery().count();
//
//        return ToWeb.buildResult().setRows(
//                ToWeb.Rows.buildRows().setCurrent(page)
//                        .setTotalPages((int) (count/rowSize+1))
//                        .setTotalRows(count)
//                        .setList(list)
//                        .setRowSize(rowSize)
//        );
//    }
//

    /**
    * 功能描述: 删除流程模型Model
    * @author: 庞留杰
    * @param   id （ModelId）
    * @exception
    * @date: 2020/9/10 10:26
    */
    @PostMapping("deleteModel/{id}")
    public Result deleteModel(@PathVariable("id")String id){
        modelService.deleteModel(id);
        return new ResultUtil<>().setSuccessMsg("删除成功");
    }

    /**
     * 发布
     * @param id
     * @return
     * @throws Exception
     */
    @PostMapping("/deploy/{id}")
    public Object deploy(@PathVariable("id")String id) throws Exception {
        //获取模型
        org.flowable.ui.modeler.domain.Model modelData = modelService.getModel(id);
        byte[] bytes = modelService.getBpmnXML(modelData);
        if (bytes == null) {
            return new ResultUtil<>().setErrorMsg("模型数据为空，请先设计流程并成功保存，再进行发布。");
        }
        BpmnModel model = modelService.getBpmnModel(modelData);
        if(model.getProcesses().size()==0){
            return new ResultUtil<>().setErrorMsg("数据模型不符要求，请至少设计一条主线流程。");
        }
        byte[] bpmnBytes = modelService.getBpmnXML(modelData);
        //发布流程
        String processName = modelData.getName() + ".bpmn20.xml";
        Deployment deployment = null;
        try {
            deployment = repositoryService.createDeployment()
                    .name(modelData.getName())
                    .addString(processName, new String(bpmnBytes, "UTF-8"))
                    .deploy();
        }catch (FlowableException e){
            return new ResultUtil<>().setErrorMsg("部署Flowable异常，异常信息："+e);
        }

        Model model1 = modelService.saveModel(modelData);

        log.info("deployment-id:{}",deployment.getId());
        log.info("deployment-Key:{}",deployment.getKey());
        log.info("model1-id:{}",model1.getId());
        log.info("model1-Key:{}",model1.getKey());
        //deployment-id:07cb1a9f-f0fb-11ea-9afa-405bd8b55521
        //deployment-Key:null
        //model1-id:e45331eb-f0f6-11ea-9870-405bd8b55521
        //model1-Key:level001
        return new ResultUtil<>().setSuccessMsg("成功");
    }

//
//    @Override
//    public Object postOne(@RequestBody Model entity) {
//        throw new UnsupportedOperationException();
//    }
//
//    @Override
//    public Object putOne(@PathVariable("id") String s, @RequestBody Model entity) {
//        throw new UnsupportedOperationException();
//    }
//
//    @Override
//    public Object patchOne(@PathVariable("id") String s, @RequestBody Model entity) {
//        throw new UnsupportedOperationException();
//    }




    @Autowired
    private RuntimeService runtimeService;

    @GetMapping("/resource")
    //查看流程部署图片【只是一个图片，不会显示执行到那个节点】
    // procDefId：流程定义ID;proInsId ：流程实例ID;  resType：资源类型(xml|image)
    // http://localhost:8888/models/resource?procDefId=askForLeave:1:8b0c7d46-f30c-11ea-a46e-405bd8b55521&proInsId=c6dc39f7-f30c-11ea-a46e-405bd8b55521&resType=image
    public void resourceRead(String procDefId, String proInsId, String resType, HttpServletResponse response) throws Exception {
        if (StringUtils.isBlank(procDefId)){
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(proInsId).singleResult();
            procDefId = processInstance.getProcessDefinitionId();
        }
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefId).singleResult();
        String resourceName = "";
        if (resType.equals("image")) {
            resourceName = processDefinition.getDiagramResourceName();
        } else if (resType.equals("xml")) {
            resourceName = processDefinition.getResourceName();
        }
        InputStream resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), resourceName);
        byte[] b = new byte[1024];
        int len = -1;
        while ((len = resourceAsStream.read(b, 0, 1024)) != -1) {
            response.getOutputStream().write(b, 0, len);
        }
    }


    /* 下载xml
     * deploymentId:流程定义ID
     * 摘抄源码：org.flowable.ui.modeler.rest.app -> /rest/models/{processModelId}/bpmn20
    * */
    @GetMapping("/resource/downloadXml/{deploymentId}")
    public void downloadXml(HttpServletResponse response, HttpServletRequest request, @PathVariable("deploymentId") String processDefinitionId){
        log.info("》》》》下载执行了》》》》");
        String processModelId = processDefinitionId;
        if (processModelId == null) {
            throw new BadRequestException("No process model id provided");
        }
        Model model = modelService.getModel(processModelId);
        String name = model.getName().replaceAll(" ", "_") + ".bpmn20.xml";
        String encodedName = null;
        try {
            encodedName = "UTF-8''" + URLEncoder.encode(name, "UTF-8");
        } catch (Exception e) {
            log.warn("编码名称失败 " + name);
        }
        String contentDispositionValue = "attachment; filename=" + name;
        if (encodedName != null) {
            contentDispositionValue += "; filename*=" + encodedName;
        }
        response.setHeader("Content-Disposition", contentDispositionValue);
        if (model.getModelEditorJson() != null) {
            try {
                ServletOutputStream servletOutputStream = response.getOutputStream();
                response.setContentType("application/xml");
                BpmnModel bpmnModel = modelService.getBpmnModel(model);
                byte[] xmlBytes = modelService.getBpmnXML(bpmnModel);
                BufferedInputStream in = new BufferedInputStream(new ByteArrayInputStream(xmlBytes));

                byte[] buffer = new byte[8096];
                while (true) {
                    int count = in.read(buffer);
                    if (count == -1) {
                        break;
                    }
                    servletOutputStream.write(buffer, 0, count);
                }
                // Flush and close stream
                servletOutputStream.flush();
                servletOutputStream.close();
            } catch (BaseModelerRestException e) {
                throw e;
            } catch (Exception e) {
                log.error("Could not generate BPMN 2.0 XML", e);
                throw new InternalServerErrorException("Could not generate BPMN 2.0 xml");
            }
        }
    }

}
